package gee

import (
	"fmt"
	"net/http"

	jsoniter "github.com/json-iterator/go"
)

type Context struct {
	W   http.ResponseWriter
	Req *http.Request
	//request
	Path   string
	Method string
	//response
	StatusCode int
	//解析后的参数
	Params map[string]string
	//中间件处理
	handles []HandleFunc
	//中间件序号,避免中间件中嵌套用户定义的路由所映射的handle时陷入无限次调用的死循环
	index int

	engine *Engine
}

//由于go采用的是垃圾收集器自动回收内存，对于变成地址类型来使用的变量都会被认为堆空间变量
//我猜测这是为了降低编译器对变量生命周期判断的复炸程度，简化设计。关键词 逃逸分析
func NewContext(w http.ResponseWriter, r *http.Request) *Context {
	return &Context{
		W:       w,
		Req:     r,
		Path:    r.URL.Path,
		Method:  r.Method,
		Params:  make(map[string]string),
		handles: make([]HandleFunc, 0),
		index:   -1,
	}
}

//通过next调换中间件的作用范围和作用点，实现中间件能在路由处理函数的前中后点起作用。
func (context *Context) Next() {
	//避免在反复调用第一个中间件或路由处理函数，陷入死循环。
	context.index++
	for ; context.index < len(context.handles); context.index++ {
		context.handles[context.index](context)
	}
}

func (context *Context) Json(statusCode int, con map[string]any) {
	context.W.Header().Set("Content-Type", "application/json")
	context.W.WriteHeader(statusCode)
	context.StatusCode = statusCode
	encode := jsoniter.NewEncoder(context.W)
	if err := encode.Encode(con); err != nil {
		panic(err)
	}
}

func (context *Context) PostForm(key string) string {
	return context.Req.PostFormValue(key)
}

func (context *Context) Query(key string) string {
	return context.Req.FormValue(key)
}

func (context *Context) HTML(statusCode int, html string, data any) {
	context.W.Header().Set("Content-Type", "text/html")
	context.W.WriteHeader(statusCode)
	context.StatusCode = statusCode
	if err := context.engine.htmlTemplates.ExecuteTemplate(context.W, html, data); err != nil {
		context.String(500, err.Error())
	}
	// doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
	// if err != nil {
	// 	log.Fatal(err)
	// }
	// content := doc.Find("h1").Text()
}

func (context *Context) String(statusCode int, format string, a ...any) {
	content := fmt.Sprintf(format, a...)
	context.W.Header().Set("Content-Type", "text/plain")
	context.W.WriteHeader(statusCode)
	context.StatusCode = statusCode
	context.W.Write([]byte(content))
}

func (context *Context) Data(statusCode int, data []byte) {
	context.W.WriteHeader(statusCode)
	context.StatusCode = statusCode
	context.W.Write(data)
}

func (context *Context) Param(key string) string {
	result, _ := context.Params[key]
	return result
}
